﻿using HslCommunication.ModBus;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace 进窑工位采集服务
{
   public class ModBus采集类1
    {
        private static ModBus采集类1 modBus;

        ModbusTcpNet busTcpClient;   // 端口号502，站号1

        byte[] data = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes("00 00 00 00 00 06 01 02 00 00 00 08");

        string ip;
        private bool isReadingPlc = false;              // 是否启动的标志，可以用来暂停项目

        private bool isReadRandom = false;              // 是否随机数据

        private Random random = new Random();

        private int failed = 0;                          // 连续失败此处，连续三次失败就报警

        private Thread threadReadPlc = null;                                               // 后台读取PLC的线程

        private int _采集周期 = 500;                          // 连续失败此处，连续三次失败就报警



        #region 构建函数
        // 定义一个标识确保线程同步
        // 定义私有构造函数，使外界不能创建该类实例
        private ModBus采集类1(string IP, string 采集周期)
        {
            busTcpClient = new ModbusTcpNet(IP);   // 端口号502，站号1

            _采集周期 = Convert.ToInt32(采集周期);

              ip = IP;

            //busTcpClient.ConnectServer();

            // 启动后台读取的线程
            threadReadPlc = new Thread(new System.Threading.ThreadStart(ThreadBackgroundReadPlc));
            threadReadPlc.IsBackground = true;
            threadReadPlc.Priority = ThreadPriority.AboveNormal;
            threadReadPlc.Start();
        }

        #endregion

        #region 单例模式
        /// <summary>
        /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
        /// </summary>
        /// <returns></returns>

        private static readonly object locker = new object();

        //public bool IsReadingPlc { get; set; } = false;
        public bool IsReadRandom { get => isReadRandom; set => isReadRandom = value; }
        public bool IsReadingPlc { get => isReadingPlc; set => isReadingPlc = value; }

        public static ModBus采集类1 GetInstance(string IP, string 采集周期)
        {
            // 当第一个线程运行到这里时，此时会对locker对象 "加锁"，
            // 当第二个线程运行该方法时，首先检测到locker对象为"加锁"状态，该线程就会挂起等待第一个线程解锁
            // lock语句运行完之后（即线程运行完之后）会对该对象"解锁"
            // 双重锁定只需要一句判断就可以了
            if (modBus == null)
            {
                lock (locker)
                {
                    // 如果类的实例不存在则创建，否则直接返回
                    if (modBus == null)
                    {
                        modBus = new ModBus采集类1(IP, 采集周期);
                    }
                }
            }
            return modBus;
        } 
        #endregion

        #region 采集数据
        public delegate void 采集数据委托(string str);

        public event 采集数据委托 采集事件;
        protected virtual void On采集事件触发(string str)
        {
            if (采集事件 != null)
            {
                采集事件(str); /* 事件被触发 */
            }
            else
            {
                Console.WriteLine("event not fire");
                Console.ReadKey(); /* 回车继续 */
            }
        } 


        #endregion

        #region 错误日记
        public delegate void 错误日记委托(string str, Exception ex);

        public event 错误日记委托 错误日记事件;
        protected virtual void On错误日记事件触发(string str, Exception ex)
        {
            if (错误日记事件 != null)
            {
                错误日记事件(str,ex); /* 事件被触发 */
            }
            else
            {
                Console.WriteLine("event not fire");
                Console.ReadKey(); /* 回车继续 */
            }
        }
        #endregion

        #region 重启事件
        public delegate void 重启委托(string str);

        public event 重启委托 重启事件;
        protected virtual void On重启事件触发(string str, Exception ex)
        {
            if (重启事件 != null)
            {
                重启事件(str); /* 事件被触发 */
            }
            else
            {
                Console.WriteLine("event not fire");
                Console.ReadKey(); /* 回车继续 */
            }
        }
        #endregion

        #region MyRegion
        //public void Read()
        //{

        //    HslCommunication.OperateResult<byte[]> read = busTcpClient.ReadFromCoreServer(data);

        //    if (read.IsSuccess)
        //    {
        //        // 获取结果，并转化为Hex字符串，方便显示
        //        //string result = HslCommunication.BasicFramework.SoftBasic.ByteToHexString(read.Content, ' ');

        //        byte[] responseHeader = read.Content;

        //        responseHeader[9].ToString();
        //        if (responseHeader[9].ToString() == "0")
        //        {

        //        }
        //        else
        //        {
        //            On采集事件触发(responseHeader[9].ToString());
        //        }

        //    }
        //    else
        //    {
        //        //#region 错误日记
        //        //LogManager.LogInfo("读取工控机状态", "怎么会是空呢？");
        //        //#endregion


        //        On错误日记事件触发("读取工控机状态;读取没成功;");

        //    }
        //    Thread.Sleep(200);
        //}

        #endregion
        private void ThreadBackgroundReadPlc()
        {

            // 此处假设我们读取的是西门子PLC的数据，其实三菱的数据读取原理是一样的，可以仿照西门子的开发

            /**************************************************************************************************
             * 
             *    假设一：M100，M101存储了一个温度值，举例，100.5℃数据为1005
             *    假设二：M102存储了设备启停信号，0为停止，1为启动
             *    假设三：M103-M106存储了一个产量值，举例：12345678
             * 
             **************************************************************************************************/
            string oldvalue="";
            while (true)
            {
                if (IsReadingPlc)
                {

                    // 这里仅仅演示了西门子的数据读取
                    // 事实上你也可以改成三菱的，无非解析数据的方式不一致而已，其他数据推送代码都是一样的



                    HslCommunication.OperateResult<string> read = null; //siemensTcpNet.Read( "M100", 7 );

                    if (IsReadRandom)
                    {
                        // 当没有测试的设备的时候，此处就演示读取随机数的情况
                        read = HslCommunication.OperateResult.CreateSuccessResult(random.Next(100).ToString());
                    }
                    else
                    {
                        //HslCommunication.OperateResult<byte[]> tmp = siemensTcpNet.Read("M100", 7);
                        HslCommunication.OperateResult<byte[]> tmp = busTcpClient.ReadFromCoreServer(data);

                        if (tmp.IsSuccess)
                        {
                            //double temp1 = siemensTcpNet.ByteTransform.TransInt16(tmp.Content, 0) / 10.0;
                            //bool machineEnable = tmp.Content[2] != 0x00;
                            byte[] vs = tmp.Content;

                            //read = HslCommunication.OperateResult.CreateSuccessResult(new JObject()
                            //{
                            //    {"station",new JValue(vs[9].ToString()) },
                            //});

                            read = HslCommunication.OperateResult.CreateSuccessResult(vs[9].ToString());
                        }
                        else
                        {
                            read = HslCommunication.OperateResult.CreateFailedResult<string>(tmp);
                        }
                    }


                    if (read.IsSuccess)
                    {
                        failed = 0;                                                              // 读取失败次数清空
                        //pushServer.PushString("A", read.Content.ToString());    // 推送数据，关键字为A
                        //ShowReadContent(read.Content);                                         // 在主界面进行显示，此处仅仅是测试，实际项目中不建议在服务端显示数据信息

                        if (read.Content!= oldvalue)
                        {
                            oldvalue = read.Content;
                            On采集事件触发(read.Content);
                        }

                       


                    }
                    else
                    {
                        failed++;
                        ShowFailedMessage(failed);                             // 显示出来读取失败的情况
                    }
                }

                Thread.Sleep(_采集周期);                            // 两次读取的时间间隔
            }
        }

        private void ShowFailedMessage(int failed)
        {
            if (failed>10)
            {
                On错误日记事件触发("读取工控机错误", new Exception("错误次数：" + failed.ToString()));

                IsReadingPlc = false;
                //System.Diagnostics.Process.Start("D:\\管片工位采集服务(十四局)-分支\\进窑工位采集服务\\进窑工位采集服务\\bin\\Debug\\进窑工位采集服务.exe", "5");
                重启事件("读取工控机错误重启");
            }
        }
    }
}
